// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include <sstream>
#include <algorithm>
#include <map>
#include <regex>

#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/wire_format.h>
#include <google/protobuf/wire_format_lite.h>

#include <google/protobuf/compiler/csharp/csharp_options.h>
#include <google/protobuf/compiler/csharp/csharp_doc_comment.h>
#include <google/protobuf/compiler/csharp/csharp_enum.h>
#include <google/protobuf/compiler/csharp/csharp_field_base.h>
#include <google/protobuf/compiler/csharp/csharp_helpers.h>
#include <google/protobuf/compiler/csharp/csharp_message.h>
#include <google/protobuf/compiler/csharp/csharp_names.h>
#include <google/protobuf/compiler/csharp_shioho/shioho_msg.cc>

extern std::vector<MsgInfo> vec;
using namespace std;

namespace google
{
  namespace protobuf
  {
    namespace compiler
    {
      namespace csharp
      {

        bool CompareFieldNumbers(const FieldDescriptor *d1, const FieldDescriptor *d2)
        {
          return d1->number() < d2->number();
        }

        MessageGenerator::MessageGenerator(const Descriptor *descriptor,
                                           const Options *options)
            : SourceGeneratorBase(options),
              descriptor_(descriptor),
              has_bit_field_count_(0),
              end_tag_(GetGroupEndTag(descriptor)),
              has_extension_ranges_(descriptor->extension_range_count() > 0)
        {
          // fields by number
          for (int i = 0; i < descriptor_->field_count(); i++)
          {
            fields_by_number_.push_back(descriptor_->field(i));
          }
          std::sort(fields_by_number_.begin(), fields_by_number_.end(),
                    CompareFieldNumbers);

          int presence_bit_count = 0;
          for (int i = 0; i < descriptor_->field_count(); i++)
          {
            const FieldDescriptor *field = descriptor_->field(i);
            if (RequiresPresenceBit(field))
            {
              presence_bit_count++;
              if (has_bit_field_count_ == 0 || (presence_bit_count % 32) == 0)
              {
                has_bit_field_count_++;
              }
            }
          }
        }

        MessageGenerator::~MessageGenerator()
        {
        }

        std::string MessageGenerator::class_name()
        {
          return descriptor_->name();
        }

        std::string MessageGenerator::full_class_name()
        {
          return GetClassName(descriptor_);
        }

        const std::vector<const FieldDescriptor *> &MessageGenerator::fields_by_number()
        {
          return fields_by_number_;
        }

        void MessageGenerator::AddDeprecatedFlag(io::Printer *printer)
        {
          if (descriptor_->options().deprecated())
          {
            printer->Print("[global::System.ObsoleteAttribute]\n");
          }
        }

        void MessageGenerator::AddSerializableAttribute(io::Printer *printer)
        {
          if (this->options()->serializable)
          {
            printer->Print("[global::System.SerializableAttribute]\n");
          }
        }

        void CheckRepeateProtocolID(vector<MsgInfo> &v, MsgInfo info)
        {
          for (size_t i = 0, length = v.size(); i < length; i++)
          {
            if (v[i].protocolID == info.protocolID)
            {
              std::cout << "\nError: Message protocolID = " << info.protocolID << " repeat!!\n"
                        << "	[Message old]:" << v[i].className << "\n"
                        << "	[Message new]:" << info.className << "\n"
                        << std::endl;
              system("pause");
              exit(0);
            }
          }
          v.push_back(info);
        }

        string MessageGenerator::GetProtocolID()
        {
          SourceLocation location;
          if (descriptor_->GetSourceLocation(&location))
          {
            std::regex pattern("^\\s*id\\s*=\\s*(\\d+)\\s*$", std::regex::icase);
            std::match_results<string::const_iterator> result;

            if (std::regex_search(location.leading_comments, result, pattern))
            {
              return result[1];
            }
          }
          return "";
        }

        void MessageGenerator::Generate(io::Printer *printer)
        {
          std::map<std::string, std::string> vars;
          vars["class_name"] = class_name();
          vars["access_level"] = class_access_level();

          WriteMessageDocComment(printer, descriptor_);
          AddDeprecatedFlag(printer);
          AddSerializableAttribute(printer);

          bool isResponse = false;
          bool isRequest = false;
          bool isPush = false;
          // proto收集
          string protocolID = GetProtocolID();
          if (protocolID != "")
          {
            string responseName = "Response";
            string requestName = "Request";
            string pushName = "Push";
            string protoName = vars["class_name"];
            string::size_type respPos = protoName.find(responseName);
            string::size_type reqPos = protoName.find(requestName);
            string::size_type pushPos = protoName.find(pushName);
            isResponse = respPos != string::npos;
            isRequest = reqPos != string::npos;
            isPush = pushPos != string::npos;

            if (!isResponse && !isRequest && !isPush)
            {
              std::cout << "\nError: proto name is not equal Request or Response or Push :" << protoName << "\n"
                        << std::endl;

              system("pause");
              exit(0);
            }

            MsgInfo info;
            info.protocolID = atoi(protocolID.c_str());
            info.isPush = isPush;
            if (isRequest)
            {
              info.className = protoName.replace(reqPos, requestName.length(), "");
            }
            else if (isResponse)
            {
              info.className = protoName.replace(respPos, responseName.length(), "");
            }
            else if (isPush)
            {
              info.className = protoName.replace(pushPos, pushName.length(), "");
            }

            info.controllerName = info.className + "MsgController";

            CheckRepeateProtocolID(vec, info);
          }

          printer->Print(
              vars,
              "$access_level$ sealed partial class $class_name$ : ");

          if (has_extension_ranges_)
          {
            printer->Print(vars, "pb::IExtendableMessage<$class_name$>\n");
          }
          else
          {
            printer->Print(vars, "pb::IMessage<$class_name$>\n");
          }
          printer->Print("#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE\n");
          printer->Print("    , pb::IBufferMessage\n");
          printer->Print("#endif\n");
          printer->Print("{\n");
          printer->Indent();

          // All static fields and properties
          printer->Print(
              vars,
              "private static readonly pb::MessageParser<$class_name$> _parser = new pb::MessageParser<$class_name$>(() => new $class_name$());\n");

          printer->Print(
              "private pb::UnknownFieldSet _unknownFields;\n");

          if (has_extension_ranges_)
          {
            if (IsDescriptorProto(descriptor_->file()))
            {
              printer->Print(vars, "internal pb::ExtensionSet<$class_name$> _extensions;\n"); // CustomOptions compatibility
            }
            else
            {
              printer->Print(vars, "private pb::ExtensionSet<$class_name$> _extensions;\n");
            }

            // a read-only property for fast
            // retrieval of the set in IsInitialized
            printer->Print(vars,
                           "private pb::ExtensionSet<$class_name$> _Extensions { get { "
                           "return _extensions; } }\n");
          }

          for (int i = 0; i < has_bit_field_count_; i++)
          {
            // don't use arrays since all arrays are heap allocated, saving allocations
            // use ints instead of bytes since bytes lack bitwise operators, saving casts
            printer->Print("private int _hasBits$i$;\n", "i", StrCat(i));
          }

          WriteGeneratedCodeAttributes(printer);

          printer->Print(
              vars,
              "public static pb::MessageParser<$class_name$> Parser { get { return _parser; } }\n\n");

          // Access the message descriptor via the relevant file descriptor or containing message descriptor.
          if (!descriptor_->containing_type())
          {
            vars["descriptor_accessor"] = GetReflectionClassName(descriptor_->file()) + ".Descriptor.MessageTypes[" + StrCat(descriptor_->index()) + "]";
          }
          else
          {
            vars["descriptor_accessor"] = GetClassName(descriptor_->containing_type()) + ".Descriptor.NestedTypes[" + StrCat(descriptor_->index()) + "]";
          }

          WriteGeneratedCodeAttributes(printer);
          printer->Print(
              vars,
              "public static pbr::MessageDescriptor Descriptor {\n"
              "  get { return $descriptor_accessor$; }\n"
              "}\n"
              "\n");
          WriteGeneratedCodeAttributes(printer);
          printer->Print(
              vars,
              "pbr::MessageDescriptor pb::IMessage.Descriptor {\n"
              "  get { return Descriptor; }\n"
              "}\n"
              "\n");

          // Parameterless constructor and partial OnConstruction method.
          WriteGeneratedCodeAttributes(printer);
          printer->Print(
              vars,
              "public $class_name$() {\n"
              "  OnConstruction();\n"
              "}\n\n"
              "partial void OnConstruction();\n\n");

          GenerateCloningCode(printer);
          GenerateFreezingCode(printer);

          // Fields/properties
          for (int i = 0; i < descriptor_->field_count(); i++)
          {
            const FieldDescriptor *fieldDescriptor = descriptor_->field(i);

            // Rats: we lose the debug comment here :(
            printer->Print(
                "/// <summary>Field number for the \"$field_name$\" field.</summary>\n"
                "public const int $field_constant_name$ = $index$;\n",
                "field_name", fieldDescriptor->name(),
                "field_constant_name", GetFieldConstantName(fieldDescriptor),
                "index", StrCat(fieldDescriptor->number()));
            std::unique_ptr<FieldGeneratorBase> generator(
                CreateFieldGeneratorInternal(fieldDescriptor));
            generator->GenerateMembers(printer);
            printer->Print("\n");
          }

          // oneof properties (for real oneofs, which come before synthetic ones)
          for (int i = 0; i < descriptor_->real_oneof_decl_count(); i++)
          {
            const OneofDescriptor *oneof = descriptor_->oneof_decl(i);
            vars["name"] = UnderscoresToCamelCase(oneof->name(), false);
            vars["property_name"] = UnderscoresToCamelCase(oneof->name(), true);
            vars["original_name"] = oneof->name();
            printer->Print(
                vars,
                "private object $name$_;\n"
                "/// <summary>Enum of possible cases for the \"$original_name$\" oneof.</summary>\n"
                "public enum $property_name$OneofCase {\n");
            printer->Indent();
            printer->Print("None = 0,\n");
            for (int j = 0; j < oneof->field_count(); j++)
            {
              const FieldDescriptor *field = oneof->field(j);
              printer->Print("$oneof_case_name$ = $index$,\n",
                             "oneof_case_name", GetOneofCaseName(field),
                             "index", StrCat(field->number()));
            }
            printer->Outdent();
            printer->Print("}\n");
            // TODO: Should we put the oneof .proto comments here?
            // It's unclear exactly where they should go.
            printer->Print(
                vars,
                "private $property_name$OneofCase $name$Case_ = $property_name$OneofCase.None;\n");
            WriteGeneratedCodeAttributes(printer);
            printer->Print(
                vars,
                "public $property_name$OneofCase $property_name$Case {\n"
                "  get { return $name$Case_; }\n"
                "}\n\n");
            WriteGeneratedCodeAttributes(printer);
            printer->Print(
                vars,
                "public void Clear$property_name$() {\n"
                "  $name$Case_ = $property_name$OneofCase.None;\n"
                "  $name$_ = null;\n"
                "}\n\n");
          }

          // Standard methods
          GenerateFrameworkMethods(printer);
          GenerateMessageSerializationMethods(printer);
          GenerateMergingMethods(printer);

          if (has_extension_ranges_)
          {
            printer->Print(
                vars,
                "public TValue GetExtension<TValue>(pb::Extension<$class_name$, "
                "TValue> extension) {\n"
                "  return pb::ExtensionSet.Get(ref _extensions, extension);\n"
                "}\n"
                "public pbc::RepeatedField<TValue> "
                "GetExtension<TValue>(pb::RepeatedExtension<$class_name$, TValue> "
                "extension) {\n"
                "  return pb::ExtensionSet.Get(ref _extensions, extension);\n"
                "}\n"
                "public pbc::RepeatedField<TValue> "
                "GetOrInitializeExtension<TValue>(pb::RepeatedExtension<$class_name$, "
                "TValue> extension) {\n"
                "  return pb::ExtensionSet.GetOrInitialize(ref _extensions, "
                "extension);\n"
                "}\n"
                "public void SetExtension<TValue>(pb::Extension<$class_name$, TValue> "
                "extension, TValue value) {\n"
                "  pb::ExtensionSet.Set(ref _extensions, extension, value);\n"
                "}\n"
                "public bool HasExtension<TValue>(pb::Extension<$class_name$, TValue> "
                "extension) {\n"
                "  return pb::ExtensionSet.Has(ref _extensions, extension);\n"
                "}\n"
                "public void ClearExtension<TValue>(pb::Extension<$class_name$, "
                "TValue> extension) {\n"
                "  pb::ExtensionSet.Clear(ref _extensions, extension);\n"
                "}\n"
                "public void "
                "ClearExtension<TValue>(pb::RepeatedExtension<$class_name$, TValue> "
                "extension) {\n"
                "  pb::ExtensionSet.Clear(ref _extensions, extension);\n"
                "}\n\n");
          }

          // Nested messages and enums
          if (HasNestedGeneratedTypes())
          {
            printer->Print(
                vars,
                "#region Nested types\n"
                "/// <summary>Container for nested types declared in the $class_name$ message type.</summary>\n");
            WriteGeneratedCodeAttributes(printer);
            printer->Print("public static partial class Types {\n");
            printer->Indent();
            for (int i = 0; i < descriptor_->enum_type_count(); i++)
            {
              EnumGenerator enumGenerator(descriptor_->enum_type(i), this->options());
              enumGenerator.Generate(printer);
            }
            for (int i = 0; i < descriptor_->nested_type_count(); i++)
            {
              // Don't generate nested types for maps...
              if (!IsMapEntryMessage(descriptor_->nested_type(i)))
              {
                MessageGenerator messageGenerator(
                    descriptor_->nested_type(i), this->options());
                messageGenerator.Generate(printer);
              }
            }
            printer->Outdent();
            printer->Print("}\n"
                           "#endregion\n"
                           "\n");
          }

          if (descriptor_->extension_count() > 0)
          {
            printer->Print(
                vars,
                "#region Extensions\n"
                "/// <summary>Container for extensions for other messages declared in the $class_name$ message type.</summary>\n");
            WriteGeneratedCodeAttributes(printer);
            printer->Print("public static partial class Extensions {\n");
            printer->Indent();
            for (int i = 0; i < descriptor_->extension_count(); i++)
            {
              std::unique_ptr<FieldGeneratorBase> generator(
                  CreateFieldGeneratorInternal(descriptor_->extension(i)));
              generator->GenerateExtensionCode(printer);
            }
            printer->Outdent();
            printer->Print(
                "}\n"
                "#endregion\n"
                "\n");
          }

          printer->Outdent();
          printer->Print("}\n");
          printer->Print("\n");
        }

        // Helper to work out whether we need to generate a class to hold nested types/enums.
        // Only tricky because we don't want to generate map entry types.
        bool MessageGenerator::HasNestedGeneratedTypes()
        {
          if (descriptor_->enum_type_count() > 0)
          {
            return true;
          }
          for (int i = 0; i < descriptor_->nested_type_count(); i++)
          {
            if (!IsMapEntryMessage(descriptor_->nested_type(i)))
            {
              return true;
            }
          }
          return false;
        }

        void MessageGenerator::GenerateCloningCode(io::Printer *printer)
        {
          std::map<std::string, std::string> vars;
          WriteGeneratedCodeAttributes(printer);
          vars["class_name"] = class_name();
          printer->Print(
              vars,
              "public $class_name$($class_name$ other) : this() {\n");
          printer->Indent();
          for (int i = 0; i < has_bit_field_count_; i++)
          {
            printer->Print("_hasBits$i$ = other._hasBits$i$;\n", "i", StrCat(i));
          }
          // Clone non-oneof fields first (treating optional proto3 fields as non-oneof)
          for (int i = 0; i < descriptor_->field_count(); i++)
          {
            const FieldDescriptor *field = descriptor_->field(i);
            if (field->real_containing_oneof())
            {
              continue;
            }
            std::unique_ptr<FieldGeneratorBase> generator(CreateFieldGeneratorInternal(field));
            generator->GenerateCloningCode(printer);
          }
          // Clone just the right field for each real oneof
          for (int i = 0; i < descriptor_->real_oneof_decl_count(); ++i)
          {
            const OneofDescriptor *oneof = descriptor_->oneof_decl(i);
            vars["name"] = UnderscoresToCamelCase(oneof->name(), false);
            vars["property_name"] = UnderscoresToCamelCase(oneof->name(), true);
            printer->Print(vars, "switch (other.$property_name$Case) {\n");
            printer->Indent();
            for (int j = 0; j < oneof->field_count(); j++)
            {
              const FieldDescriptor *field = oneof->field(j);
              std::unique_ptr<FieldGeneratorBase> generator(CreateFieldGeneratorInternal(field));
              vars["oneof_case_name"] = GetOneofCaseName(field);
              printer->Print(
                  vars,
                  "case $property_name$OneofCase.$oneof_case_name$:\n");
              printer->Indent();
              generator->GenerateCloningCode(printer);
              printer->Print("break;\n");
              printer->Outdent();
            }
            printer->Outdent();
            printer->Print("}\n\n");
          }
          // Clone unknown fields
          printer->Print(
              "_unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);\n");
          if (has_extension_ranges_)
          {
            printer->Print(
                "_extensions = pb::ExtensionSet.Clone(other._extensions);\n");
          }

          printer->Outdent();
          printer->Print("}\n\n");

          WriteGeneratedCodeAttributes(printer);
          printer->Print(
              vars,
              "public $class_name$ Clone() {\n"
              "  return new $class_name$(this);\n"
              "}\n\n");
        }

        void MessageGenerator::GenerateFreezingCode(io::Printer *printer)
        {
        }

        void MessageGenerator::GenerateFrameworkMethods(io::Printer *printer)
        {
          std::map<std::string, std::string> vars;
          vars["class_name"] = class_name();

          // Equality
          WriteGeneratedCodeAttributes(printer);
          printer->Print(
              vars,
              "public override bool Equals(object other) {\n"
              "  return Equals(other as $class_name$);\n"
              "}\n\n");
          WriteGeneratedCodeAttributes(printer);
          printer->Print(
              vars,
              "public bool Equals($class_name$ other) {\n"
              "  if (ReferenceEquals(other, null)) {\n"
              "    return false;\n"
              "  }\n"
              "  if (ReferenceEquals(other, this)) {\n"
              "    return true;\n"
              "  }\n");
          printer->Indent();
          for (int i = 0; i < descriptor_->field_count(); i++)
          {
            std::unique_ptr<FieldGeneratorBase> generator(
                CreateFieldGeneratorInternal(descriptor_->field(i)));
            generator->WriteEquals(printer);
          }
          for (int i = 0; i < descriptor_->real_oneof_decl_count(); i++)
          {
            printer->Print("if ($property_name$Case != other.$property_name$Case) return false;\n",
                           "property_name", UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true));
          }
          if (has_extension_ranges_)
          {
            printer->Print(
                "if (!Equals(_extensions, other._extensions)) {\n"
                "  return false;\n"
                "}\n");
          }
          printer->Outdent();
          printer->Print(
              "  return Equals(_unknownFields, other._unknownFields);\n"
              "}\n\n");

          // GetHashCode
          // Start with a non-zero value to easily distinguish between null and "empty" messages.
          WriteGeneratedCodeAttributes(printer);
          printer->Print(
              "public override int GetHashCode() {\n"
              "  int hash = 1;\n");
          printer->Indent();
          for (int i = 0; i < descriptor_->field_count(); i++)
          {
            std::unique_ptr<FieldGeneratorBase> generator(
                CreateFieldGeneratorInternal(descriptor_->field(i)));
            generator->WriteHash(printer);
          }
          for (int i = 0; i < descriptor_->real_oneof_decl_count(); i++)
          {
            printer->Print("hash ^= (int) $name$Case_;\n",
                           "name", UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), false));
          }
          if (has_extension_ranges_)
          {
            printer->Print(
                "if (_extensions != null) {\n"
                "  hash ^= _extensions.GetHashCode();\n"
                "}\n");
          }
          printer->Print(
              "if (_unknownFields != null) {\n"
              "  hash ^= _unknownFields.GetHashCode();\n"
              "}\n"
              "return hash;\n");
          printer->Outdent();
          printer->Print("}\n\n");

          WriteGeneratedCodeAttributes(printer);
          printer->Print(
              "public override string ToString() {\n"
              "  return pb::JsonFormatter.ToDiagnosticString(this);\n"
              "}\n\n");
        }

        void MessageGenerator::GenerateMessageSerializationMethods(io::Printer *printer)
        {
          WriteGeneratedCodeAttributes(printer);
          printer->Print(
              "public void WriteTo(pb::CodedOutputStream output) {\n");
          printer->Print("#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE\n");
          printer->Indent();
          printer->Print("output.WriteRawMessage(this);\n");
          printer->Outdent();
          printer->Print("#else\n");
          printer->Indent();
          GenerateWriteToBody(printer, false);
          printer->Outdent();
          printer->Print("#endif\n");
          printer->Print("}\n\n");

          printer->Print("#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE\n");
          WriteGeneratedCodeAttributes(printer);
          printer->Print("void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {\n");
          printer->Indent();
          GenerateWriteToBody(printer, true);
          printer->Outdent();
          printer->Print("}\n");
          printer->Print("#endif\n\n");

          WriteGeneratedCodeAttributes(printer);
          printer->Print(
              "public int CalculateSize() {\n");
          printer->Indent();
          printer->Print("int size = 0;\n");
          for (int i = 0; i < descriptor_->field_count(); i++)
          {
            std::unique_ptr<FieldGeneratorBase> generator(
                CreateFieldGeneratorInternal(descriptor_->field(i)));
            generator->GenerateSerializedSizeCode(printer);
          }

          if (has_extension_ranges_)
          {
            printer->Print(
                "if (_extensions != null) {\n"
                "  size += _extensions.CalculateSize();\n"
                "}\n");
          }

          printer->Print(
              "if (_unknownFields != null) {\n"
              "  size += _unknownFields.CalculateSize();\n"
              "}\n");

          printer->Print("return size;\n");
          printer->Outdent();
          printer->Print("}\n\n");
        }

        void MessageGenerator::GenerateWriteToBody(io::Printer *printer, bool use_write_context)
        {
          // Serialize all the fields
          for (int i = 0; i < fields_by_number().size(); i++)
          {
            std::unique_ptr<FieldGeneratorBase> generator(
                CreateFieldGeneratorInternal(fields_by_number()[i]));
            generator->GenerateSerializationCode(printer, use_write_context);
          }

          if (has_extension_ranges_)
          {
            // Serialize extensions
            printer->Print(
                use_write_context
                    ? "if (_extensions != null) {\n"
                      "  _extensions.WriteTo(ref output);\n"
                      "}\n"
                    : "if (_extensions != null) {\n"
                      "  _extensions.WriteTo(output);\n"
                      "}\n");
          }

          // Serialize unknown fields
          printer->Print(
              use_write_context
                  ? "if (_unknownFields != null) {\n"
                    "  _unknownFields.WriteTo(ref output);\n"
                    "}\n"
                  : "if (_unknownFields != null) {\n"
                    "  _unknownFields.WriteTo(output);\n"
                    "}\n");

          // TODO(jonskeet): Memoize size of frozen messages?
        }

        void MessageGenerator::GenerateMergingMethods(io::Printer *printer)
        {
          // Note:  These are separate from GenerateMessageSerializationMethods()
          //   because they need to be generated even for messages that are optimized
          //   for code size.
          std::map<std::string, std::string> vars;
          vars["class_name"] = class_name();

          WriteGeneratedCodeAttributes(printer);
          printer->Print(
              vars,
              "public void MergeFrom($class_name$ other) {\n");
          printer->Indent();
          printer->Print(
              "if (other == null) {\n"
              "  return;\n"
              "}\n");
          // Merge non-oneof fields, treating optional proto3 fields as normal fields
          for (int i = 0; i < descriptor_->field_count(); i++)
          {
            const FieldDescriptor *field = descriptor_->field(i);
            if (field->real_containing_oneof())
            {
              continue;
            }
            std::unique_ptr<FieldGeneratorBase> generator(CreateFieldGeneratorInternal(field));
            generator->GenerateMergingCode(printer);
          }
          // Merge oneof fields (for non-synthetic oneofs)
          for (int i = 0; i < descriptor_->real_oneof_decl_count(); ++i)
          {
            const OneofDescriptor *oneof = descriptor_->oneof_decl(i);
            vars["name"] = UnderscoresToCamelCase(oneof->name(), false);
            vars["property_name"] = UnderscoresToCamelCase(oneof->name(), true);
            printer->Print(vars, "switch (other.$property_name$Case) {\n");
            printer->Indent();
            for (int j = 0; j < oneof->field_count(); j++)
            {
              const FieldDescriptor *field = oneof->field(j);
              vars["oneof_case_name"] = GetOneofCaseName(field);
              printer->Print(
                  vars,
                  "case $property_name$OneofCase.$oneof_case_name$:\n");
              printer->Indent();
              std::unique_ptr<FieldGeneratorBase> generator(CreateFieldGeneratorInternal(field));
              generator->GenerateMergingCode(printer);
              printer->Print("break;\n");
              printer->Outdent();
            }
            printer->Outdent();
            printer->Print("}\n\n");
          }
          // Merge extensions
          if (has_extension_ranges_)
          {
            printer->Print("pb::ExtensionSet.MergeFrom(ref _extensions, other._extensions);\n");
          }

          // Merge unknown fields.
          printer->Print(
              "_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);\n");

          printer->Outdent();
          printer->Print("}\n\n");

          WriteGeneratedCodeAttributes(printer);
          printer->Print("public void MergeFrom(pb::CodedInputStream input) {\n");
          printer->Print("#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE\n");
          printer->Indent();
          printer->Print("input.ReadRawMessage(this);\n");
          printer->Outdent();
          printer->Print("#else\n");
          printer->Indent();
          GenerateMainParseLoop(printer, false);
          printer->Outdent();
          printer->Print("#endif\n");
          printer->Print("}\n\n");

          printer->Print("#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE\n");
          WriteGeneratedCodeAttributes(printer);
          printer->Print("void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {\n");
          printer->Indent();
          GenerateMainParseLoop(printer, true);
          printer->Outdent();
          printer->Print("}\n"); // method
          printer->Print("#endif\n\n");
        }

        void MessageGenerator::GenerateMainParseLoop(io::Printer *printer, bool use_parse_context)
        {
          std::map<std::string, std::string> vars;
          vars["maybe_ref_input"] = use_parse_context ? "ref input" : "input";

          printer->Print(
              "uint tag;\n"
              "while ((tag = input.ReadTag()) != 0) {\n"
              "  switch(tag) {\n");
          printer->Indent();
          printer->Indent();
          if (end_tag_ != 0)
          {
            printer->Print(
                "case $end_tag$:\n"
                "  return;\n",
                "end_tag", StrCat(end_tag_));
          }
          if (has_extension_ranges_)
          {
            printer->Print(vars,
                           "default:\n"
                           "  if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, $maybe_ref_input$)) {\n"
                           "    _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, $maybe_ref_input$);\n"
                           "  }\n"
                           "  break;\n");
          }
          else
          {
            printer->Print(vars,
                           "default:\n"
                           "  _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, $maybe_ref_input$);\n"
                           "  break;\n");
          }
          for (int i = 0; i < fields_by_number().size(); i++)
          {
            const FieldDescriptor *field = fields_by_number()[i];
            internal::WireFormatLite::WireType wt =
                internal::WireFormat::WireTypeForFieldType(field->type());
            uint32_t tag = internal::WireFormatLite::MakeTag(field->number(), wt);
            // Handle both packed and unpacked repeated fields with the same Read*Array call;
            // the two generated cases are the packed and unpacked tags.
            // TODO(jonskeet): Check that is_packable is equivalent to
            // is_repeated && wt in { VARINT, FIXED32, FIXED64 }.
            // It looks like it is...
            if (field->is_packable())
            {
              printer->Print(
                  "case $packed_tag$:\n",
                  "packed_tag",
                  StrCat(
                      internal::WireFormatLite::MakeTag(
                          field->number(),
                          internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED)));
            }

            printer->Print("case $tag$: {\n", "tag", StrCat(tag));
            printer->Indent();
            std::unique_ptr<FieldGeneratorBase> generator(
                CreateFieldGeneratorInternal(field));
            generator->GenerateParsingCode(printer, use_parse_context);
            printer->Print("break;\n");
            printer->Outdent();
            printer->Print("}\n");
          }
          printer->Outdent();
          printer->Print("}\n"); // switch
          printer->Outdent();
          printer->Print("}\n"); // while
        }

        // it's a waste of space to track presence for all values, so we only track them if they're not nullable
        int MessageGenerator::GetPresenceIndex(const FieldDescriptor *descriptor)
        {
          if (!RequiresPresenceBit(descriptor))
          {
            return -1;
          }

          int index = 0;
          for (int i = 0; i < fields_by_number().size(); i++)
          {
            const FieldDescriptor *field = fields_by_number()[i];
            if (field == descriptor)
            {
              return index;
            }
            if (RequiresPresenceBit(field))
            {
              index++;
            }
          }
          GOOGLE_LOG(DFATAL) << "Could not find presence index for field " << descriptor->name();
          return -1;
        }

        FieldGeneratorBase *MessageGenerator::CreateFieldGeneratorInternal(
            const FieldDescriptor *descriptor)
        {
          return CreateFieldGenerator(descriptor, GetPresenceIndex(descriptor), this->options());
        }

      } // namespace csharp
    }   // namespace compiler
  }     // namespace protobuf
} // namespace google
